{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Progress Reporting and Command Observers\n",
    "\n",
    "SimpleITK `Filter`s and other classes derived from `ProcessObject`s have the ability for user code to be executed when certain events occur. This is known as the Command and Observer design patters to implement user callbacks. This allows for the monitoring and abortion of processes as they are being executed.\n",
    "\n",
    "Consider the following image source which takes a few seconds to execute. It would be nice to quickly know how long your going to need to wait, to know if you can go get a cup of coffee."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from __future__ import print_function\n",
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "import SimpleITK as sitk\n",
    "print(sitk.Version())\n",
    "\n",
    "import sys\n",
    "import os\n",
    "import threading\n",
    "\n",
    "from myshow import myshow\n",
    "from myshow import myshow3d"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "size=256 # if this is too fast increase the size\n",
    "img = sitk.GaborSource(sitk.sitkFloat32, size=[size]*3, sigma=[size*.2]*3, mean=[size*0.5]*3, frequency=.1)\n",
    "myshow3d(img,zslices=[int(size/2)],dpi=40);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "myshow(img);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We need to add a command to display the progress reported by the `ProcessObject::GetProgress` method during the `sitkProgressEvent`. This involves three components:\n",
    "\n",
    " 1. Events\n",
    " 2. ProcessObject's methods\n",
    " 3. Commands\n",
    " \n",
    " We'll look at some examples after a brief explanation of these components.\n",
    " \n",
    "## Events\n",
    "\n",
    "The avaiable events to observed are defined in a namespace enumeration.\n",
    "\n",
    "<table>\n",
    " <tr><td>sitkAnyEvent</td><td>Occurs for all event types.</td></tr>\n",
    " <tr><td>sitkAbortEvent</td><td>Occurs after the process has been aborted, but before exiting the Execute method.</td></tr>\n",
    " <tr><td>sitkDeleteEvent</td><td>Occurs when the underlying itk::ProcessObject is deleted.</td></tr>\n",
    " <tr><td>sitkEndEvent</td><td>Occurs at then end of normal processing.</td></tr>\n",
    " <tr><td>sitkIterationEvent</td><td>Occurs with some algorithms that run for a fixed or undetermined number of iterations.</td></tr>\n",
    " <tr><td>sitkProgressEvent</td><td>Occurs when the progress changes in most process objects.</td></tr>\n",
    " <tr><td>sitkStartEvent</td><td>Occurs when then itk::ProcessObject is starting.</td></tr>\n",
    " <tr><td>sitkUserEvent</td><td>Other events may fall into this enumeration.</td></tr>\n",
    "</table>\n",
    "\n",
    "The convention of pre-fixing enums with \"sitk\" is continued, although it's getting a little crowded. \n",
    "\n",
    "C++ is more strongly typed than Python it allows for implicit conversion from an enum type to an int, but not from an int to an enum type. Care needs to be made to ensure the correct enum value is passed in Python.\n",
    "\n",
    "## ProcessObject's methods\n",
    "\n",
    "To be able to interface with the `ProcessObject` during execution, the object-oriented interface must be used to access the method of the ProcessObject. While any constant member function can be called during a command call-back there are two common methods:\n",
    "\n",
    "1. `ProcessObject::GetProgress()`\n",
    "2. `ProcessObject::Abort()`\n",
    "\n",
    "The methods are only valid during the `Command` while a process is being executed, or when the process is not in the `Execute` method.\n",
    "\n",
    "Additionally it should be noted that follow methods can *not* be called during a command or from another thread during execution `Execute` and `RemoveAllCommands`. In general the `ProcessObject` should not be modified during execution.\n",
    "\n",
    "\n",
    "## Commands\n",
    "\n",
    "The command design pattern is used to allow user code to be executed when an event occurs. It is implemented in the `Command` class. The `Command` class provides an `Execute` method to be overridden in derived classes. \n",
    "\n",
    "There are three ways to define a command with SimpleITK in Python.\n",
    "\n",
    "1. Derive from the `Command` class.\n",
    "2. Use the `PyCommand` class' `SetCallbackPyCallable` method.\n",
    "3. Use an inline `lambda` function in `ProcessOject::AddCommand`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "help(sitk.Command)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class MyCommand(sitk.Command):\n",
    "    def __init__(self):\n",
    "        # required\n",
    "        super(MyCommand,self).__init__()\n",
    "\n",
    "    def Execute(self):\n",
    "        print(\"MyCommand::Execute Called\")\n",
    "        \n",
    "cmd = MyCommand()\n",
    "cmd.Execute()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false,
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "help(sitk.PyCommand)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "cmd = sitk.PyCommand()\n",
    "cmd.SetCallbackPyCallable( lambda: print(\"PyCommand Called\") )\n",
    "cmd.Execute()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Back to watching the progress of out Gabor image source. First lets create the filter as an object"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "size=256\n",
    "filter = sitk.GaborImageSource()\n",
    "filter.SetOutputPixelType(sitk.sitkFloat32)\n",
    "filter.SetSize([size]*3)\n",
    "filter.SetSigma([size*.2]*3)\n",
    "filter.SetMean([size*0.5]*3)\n",
    "filter.SetFrequency(.1)\n",
    "img = filter.Execute()\n",
    "myshow3d(img,zslices=[int(size/2)],dpi=40);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The ProcessObject interface for the Invoker or Subject\n",
    "\n",
    "SimpleITK doesn't have a large heirachy of inheritance. It has been kept to a minimal, so there is no common `Object` or `LightObject` base class as ITK has. As most of the goals for the events have to do with observing processes, the \"Subject\" interface of the Observer patter or the \"Invoker\" part of the Command design pattern, has been added to a `ProcessObject` base class for filters.\n",
    "\n",
    "The `ProcessObject` base class has the following methods of handling commands: `AddCommand`, `RemoveAllCommands`, and `HasCommand`.\n",
    "\n",
    "Adding these functionalities are not available in the procedural interface available for SimpleITK. They are only available through the Object Oriented interface, and break the method chaining interface."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "help(sitk.ProcessObject)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Deriving from the `Command` class\n",
    "The traditional way of using Commands in ITK involves deriving from the `Command` class and adding to the `ProcessObject`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "class MyCommand(sitk.Command):\n",
    "    def __init__(self, msg):\n",
    "        # required\n",
    "        super(MyCommand,self).__init__()\n",
    "        self.msg = msg\n",
    "    \n",
    "    def __del__(self):\n",
    "        print(\"MyCommand begin deleted: \\\"{0}\\\"\".format(self.msg))\n",
    "\n",
    "    def Execute(self):\n",
    "        print(self.msg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "cmd1 = MyCommand(\"Start\")\n",
    "cmd2 = MyCommand(\"End\")\n",
    "filter.RemoveAllCommands() # this line is here so we can easily re-execute this code block\n",
    "filter.AddCommand(sitk.sitkStartEvent, cmd1)\n",
    "filter.AddCommand(sitk.sitkEndEvent, cmd2)\n",
    "filter.Execute()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A reference to the `Command` object must be maintained, or else it will be removed from the `ProcessObject`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "filter.AddCommand(sitk.sitkStartEvent, MyCommand(\"stack scope\"))\n",
    "print(\"Before Execution\")\n",
    "filter.Execute()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Using a `labmda` function as the `Command`\n",
    "In Python the `AddCommand` has been extended to accept `PyCommand` objects and implicitly creates a `PyCommand` from a callable python argument. This is **really** useful."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "filter.RemoveAllCommands() # this line is here so we can easily re-execute this code block\n",
    "filter.AddCommand(sitk.sitkStartEvent, lambda: print(\"Starting...\",end=''))\n",
    "filter.AddCommand(sitk.sitkStartEvent, lambda: sys.stdout.flush())\n",
    "filter.AddCommand(sitk.sitkEndEvent, lambda: print(\"Done\"))\n",
    "filter.Execute()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Access to ITK data during command execution\n",
    "The commands are not too useful unless you can query the filter through the SimpleITK interface. A couple status variables and methods are exposed in the SimpleITK `ProcessObject` through the polymorphic interface of the same ITK class. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "filter.RemoveAllCommands()\n",
    "filter.AddCommand(sitk.sitkProgressEvent, lambda: print(\"\\rProgress: {0:03.1f}%...\".format(100*filter.GetProgress()),end=''))\n",
    "filter.AddCommand(sitk.sitkProgressEvent, lambda: sys.stdout.flush())\n",
    "filter.AddCommand(sitk.sitkEndEvent, lambda: print(\"Done\"))\n",
    "filter.Execute()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Utilizing Jupyter Notebooks and Commands\n",
    "\n",
    "Utilization of commands and events frequently occurs with advanced integration into graphical user interfaces. Let us now export this advanced integration into Jupyter Notebooks.\n",
    "\n",
    "Jupyter notebooks support displaying output as HTML, and execution of javascript on demand. Together this can produce animation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import uuid\n",
    "from IPython.display import HTML, Javascript, display\n",
    "\n",
    "divid = str(uuid.uuid4())\n",
    "\n",
    "html_progress=\"\"\"\n",
    "<p style=\"margin:5px\">FilterName:</p>\n",
    "<div style=\"border: 1px solid black;padding:1px;margin:5px\">\n",
    "  <div id=\"{0}\" style=\"background-color:blue; width:0%%\">&nbsp;</div>\n",
    "</div>\n",
    "\"\"\".format(divid)\n",
    "\n",
    "def command_js_progress(processObject):\n",
    "    p = processObject.GetProgress()\n",
    "    display(Javascript(\"$('div#%s').width('%i%%')\" % (divid, int(p*100))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "filter.RemoveAllCommands()\n",
    "filter.AddCommand(sitk.sitkStartEvent, lambda:  display(HTML(html_progress)))\n",
    "filter.AddCommand(sitk.sitkProgressEvent, lambda: command_js_progress(filter))\n",
    "\n",
    "filter.Execute()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Support for Bi-direction JavaScript\n",
    "\n",
    "It's possible to get button in HTML to execute python code..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import uuid\n",
    "from IPython.display import HTML, Javascript, display\n",
    "\n",
    "g_Abort = False\n",
    "divid = str(uuid.uuid4())\n",
    "\n",
    "html_progress_abort=\"\"\"\n",
    "<div style=\"background-color:gainsboro; border:2px solid black;padding:15px\">\n",
    "<p style=\"margin:5px\">FilterName:</p>\n",
    "<div style=\"border: 1px solid black;padding:1px;margin:5px\">\n",
    "  <div id=\"{0}\" style=\"background-color:blue; width:0%%\">&nbsp;</div>\n",
    "</div>\n",
    "<button onclick=\"set_value()\" style=\"margin:5px\" >Abort</button>\n",
    "</div>\n",
    "\"\"\".format(divid)\n",
    "\n",
    "javascript_abort = \"\"\"\n",
    "<script type=\"text/Javascript\">\n",
    "    function set_value(){\n",
    "        var command = \"g_Abort=True\"\n",
    "        console.log(\"Executing Command: \" + command);\n",
    "        \n",
    "        var kernel = IPython.notebook.kernel;\n",
    "        kernel.execute(command);\n",
    "    }\n",
    "</script>\n",
    "\"\"\"\n",
    "\n",
    "def command_js_progress_abort(processObject):\n",
    "    p = processObject.GetProgress()\n",
    "    display(Javascript(\"$('div#%s').width('%i%%')\" % (divid, int(p*100))))\n",
    "    if g_Abort:\n",
    "        processObject.Abort()\n",
    "        \n",
    "def command_js_start_abort():        \n",
    "    g_Abort=False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "g_Abort=False\n",
    "filter.RemoveAllCommands()\n",
    "filter.AddCommand(sitk.sitkStartEvent, command_js_start_abort )\n",
    "filter.AddCommand(sitk.sitkStartEvent, lambda:  display(HTML(html_progress_abort+javascript_abort)))\n",
    "filter.AddCommand(sitk.sitkProgressEvent, lambda: command_js_progress_abort(filter))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A caveat with this approach is that the IPython kernel must continue to execute while the filter is running. So we must place the filter in a thread."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import threading\n",
    "threading.Thread( target=lambda:filter.Execute() ).start()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "While the `lambda` command are convenient, the lack for having an object to hold data can still be problematic. For example in the above code the uuid, is used to uniquely identify the HTML element. So if the filter is executed multiple times then the JavaScript update will be confused on what to update."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "#### The following shows a failure that you will want to avoid.\n",
    "threading.Thread( target=lambda:filter.Execute() ).start()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A Reusable class for IPython Progress\n",
    "\n",
    "There currently are too many caveats without support for Abort. Let us create a reusable class which will automatically generate the UUID and just display the progress."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import uuid\n",
    "from IPython.display import HTML, Javascript, display\n",
    "\n",
    "class HTMLProgressWatcher:\n",
    "    def __init__(self, po):\n",
    "        self.processObject = po\n",
    "        self.abort = False\n",
    "        \n",
    "        po.AddCommand(sitk.sitkStartEvent, lambda: self.cmdStartEvent() )\n",
    "        po.AddCommand(sitk.sitkProgressEvent, lambda: self.cmdProgressEvent() )\n",
    "        po.AddCommand(sitk.sitkEndEvent, lambda: self.cmdEndEvent() )\n",
    "        \n",
    "    def cmdStartEvent(self):\n",
    "        global sitkIPythonProgress_UUID\n",
    "        self.abort=False\n",
    "        self.divid = str(uuid.uuid4())\n",
    "        \n",
    "        try:\n",
    "            sitkIPythonProgress_UUID[self.divid] = self\n",
    "        except NameError:\n",
    "            sitkIPythonProgress_UUID = {self.divid: self}\n",
    "        \n",
    "        html_progress_abort=\"\"\"\n",
    "<p style=\"margin:5px\">{0}:</p>\n",
    "<div style=\"border: 1px solid black;padding:1px;margin:5px\">\n",
    "  <div id=\"{1}\" style=\"background-color:blue; width:0%%\">&nbsp;</div>\n",
    "</div>\n",
    "\"\"\".format(self.processObject.GetName(), self.divid)\n",
    "        \n",
    "        display(HTML(html_progress_abort+javascript_abort))\n",
    "    \n",
    "    def cmdProgressEvent(self):\n",
    "        p = self.processObject.GetProgress()\n",
    "        display(Javascript(\"$('div#%s').width('%i%%')\" % (self.divid, int(p*100))))\n",
    "        if self.abort:\n",
    "            self.processObject.Abort()\n",
    "    \n",
    "    def cmdEndEvent(self):\n",
    "        global sitkIPythonProgress_UUID\n",
    "        del sitkIPythonProgress_UUID[self.divid]\n",
    "        del self.divid\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "filter.RemoveAllCommands()\n",
    "watcher  = HTMLProgressWatcher(filter)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "filter.Execute()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "threading.Thread.start?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.4.5"
  },
  "widgets": {
   "state": {
    "9a054c9be681481883144bf87c555169": {
     "views": [
      {
       "cell_index": 3
      }
     ]
    }
   },
   "version": "1.2.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
